﻿
#region Using directives

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;

using FamilyTreeProject.Framework.Enums;
using FamilyTreeProject.Common;

#endregion

namespace FamilyTreeProject.Framework.Structures
{
  /// <summary>
  /// The Date Class is used to describe a Single Genealogical Date. 
  /// </summary>
  public class Date : IComparable
  {

    #region Fields

    private DateCalendar calendar = DateCalendar.Gregorian;
    private DateType type = DateType.Exact;
    private int? day = null;
    private int? month = null;
    private int? year = null;
    private DateTime dateTime = DateTime.Parse("1/1/00");
    private bool isBC = false;
    private bool isDateTime = false;

    #endregion

    #region Constructors

    public Date(DateCalendar calendar)
    {
      this.calendar = calendar;
    }

    #endregion

    #region Properties

    /// <summary>
    /// Gets or sets the Calendar
    /// </summary>
    public DateCalendar Calendar
    {
      get { return calendar;  }
      set { calendar = value; }
    }

    /// <summary>
    /// Gets or sets the Day
    /// </summary>
    public int? Day
    {
      get { return day; }
      set { day = value; }
    }

    /// <summary>
    /// Gets or sets the Exact Date
    /// </summary>
    public DateTime DateTime
    {
      get 
      {
        if (IsDateTime)
        {
          return dateTime;
        }
        else
        {
          throw new Exception("Date is not a DateTime value");
        }
      }
    }

    /// <summary>
    /// Gets or sets the IsBC flag
    /// </summary>
    /// <value>true/false</value>
    public bool IsBC
    {
      get { return isBC; }
      set { isBC = value; }
    }

    /// <summary>
    /// Gets the IsDateTime flag
    /// </summary>
    /// <value>true/false</value>
    public bool IsDateTime
    {
      get 
      {
        CheckIsDateTime();
        return isDateTime;
      }
    }

    /// <summary>
    /// Gets or sets the Month
    /// </summary>
    public int? Month
    {
      get { return month; }
      set { month = value;  }
    }

    /// <summary>
    /// Gets or sets the Date Type
    /// </summary>
    public DateType Type
    {
      get { return type;  }
      set { type = value; }
    }

    /// <summary>
    /// Gets or sets the Year
    /// </summary>
    public int? Year
    {
      get { return year;  }
      set { year = value; }
    }

    #endregion

    #region Public Methods

    /// <summary>
    /// Overrides the ToString method to return a formatted date string
    /// </summary>
    /// <returns></returns>
    public override string ToString()
    {
      string retValue = "";
      retValue += (type != DateType.Exact) ? type.ToString() + " " : "";

      if (IsDateTime)
      {
        retValue += dateTime.ToString("dd MMM yyyy");
      }
      else
      {
        retValue += (day.HasValue) ? day.ToString() + " " : "";
        retValue += (month.HasValue) ? DateUtil.MonthName(month.Value, calendar) + " " : "";
        retValue += (year.HasValue) ? year.ToString() : "";
        retValue += (isBC) ? " BC" : "";
      }

      return retValue;
    }


    #endregion

    #region Private Methods

    /// <summary>
    /// Checks whether the day/month/year combination represents a
    /// parseable DateTime structure
    /// </summary>
    private void CheckIsDateTime()
    {
      isDateTime = false;
      if ((day.HasValue) && (month.HasValue) && (year.HasValue))
      {
        dateTime = new DateTime(year.Value, month.Value, day.Value);
        isDateTime = true;
      }
    }

    #endregion

    #region IComparable Members

    /// <summary>
    /// CompareTo compares two dates
    /// </summary>
    /// <param name="obj">The date to compare this instance with</param>
    /// <returns>-ve, 0 or +ve</returns>
    public int CompareTo(object obj)
    {
      Int32 retValue = 0;

      if (obj is Date)
      {
        Date temp = (Date)obj;

        //If both Dates are DateTimes
        if (IsDateTime && temp.IsDateTime)
        {
          retValue = dateTime.CompareTo(temp.DateTime);
        }
        else
        {
          //Compare Years
          retValue = Nullable.Compare<int>(year ,temp.Year);

          //Compare Months
          if (retValue == 0)
          {
            retValue = Nullable.Compare<int>(month, temp.Month);
          }

          //Compare Days
          if (retValue == 0)
          {
            retValue = Nullable.Compare<int>(day, temp.Day);
          }
        }
      }
      else
        throw new ArgumentException("object is not a Date");

      return retValue;
    }

    #endregion

  }
}
